require "View/BaseCtrl"

local P = NewClass(BaseCtrl, ClassFromFile(...));


local downTime = 0.3

local rowCount = 0
local colCount = 8




function CombineGridType(t, dirMask)
	return t * 10000 + dirMask
end



local STATE_0 = 0
local STATE_1 = 0


local TYPE_NONE = 0
local TYPE_ENTER = 1
local TYPE_EXIT = 2



-- local DIRMASK_SRC = 0010
-- local DIRMASK_1 = 1010
-- local DIRMASK_L = 0011
-- local DIRMASK_T = 1101
-- local DIRMASK_X = 1111



local chessGridData = {21, 11, 12, 13, 12, 11, 12, 13, 
					   14, 11, 12, 13, 14, 11, 12, 13, 
					   14, 11, 12, 13, 14, 11, 12, 13, 
					   14, 11, 11, 22, 14, 11, 11, 22,
					}



local clearupGameConfig = 
{
	[21] = 
	{
		icon = "tex/ui/pipe/shooter1",
		dirMask = 0010,
		entrance = true,
	},
	[22] = 
	{
		icon = "tex/ui/pipe/key1",
		dirMask = 0010,
		exit = true,
	},
	[11] = 
	{
		icon = "tex/ui/pipe/pipe1",
		dirMask = 1010,
	},
	[12] = 
	{
		icon = "tex/ui/pipe/pipeL",
		dirMask = 0011,
	},
	[13] = 
	{
		icon = "tex/ui/pipe/pipeT",
		dirMask = 1101,
	},
	[14] = 
	{
		icon = "tex/ui/pipe/pipeX",
		dirMask = 1111,
	},
}

function P:gcd(a, b)
	local c = a % b
	if c == 0 then
		return b
	end

	return self:gcd(b, c)
end

function P:Init(params)
	if params and params.chessGridData then
		self.chessTypeCount = params.chessTypeCount
		chessGridData = params.chessGridData
		colCount = params.colCount
		self.mode = params.mode
		self.maxStep = params.maxStep
		self.finishCallback = params.finishCallback
	end
	self.boolIswin = false
	-- LuaSound.Play(Config.sound.stage_minigame3)
	

	rowCount = #chessGridData / colCount

	local rate = self._funObj:GetPanel().width / self._funObj:GetPanel().height
	local rootHeight = self._funObj:GetPanel().root.activeHeight
	local rootWidth = math.floor(rootHeight * rate + 0.5)


	self.chessboardWidth = rootWidth * 0.8
	self.chessboardHeight = rootHeight * 0.8


	self.gridWidth = math.min(self.chessboardHeight / (rowCount + 0), self.chessboardWidth / (colCount + 0))
	-- local worldV = self._dataObj.chessboard.transform:TransformVector(Vector3(self.gridWidth / 2, 0, 0))
	-- local camera = self._funObj:GetPanel().anchorCamera
	-- local screenPos = camera:WorldToScreenPoint(worldV)
	-- screenPos.x = math.floor(screenPos.x)
	-- worldV = camera:ScreenToWorldPoint(screenPos)
	-- local halfWidthV = self._dataObj.chessboard.transform:InverseTransformVector(worldV)
	-- self.gridWidth = halfWidthV.x * 2



	-- use gcd begin
	-- local gcdHeight =self:gcd(rootHeight, self._funObj:GetPanel().height)
	-- local gcdHeight = math.floor(rootHeight / gcdHeight + 0.5)
	-- local gcdWidth = self:gcd(rootWidth, self._funObj:GetPanel().width)
	-- local gcdWidth =math.floor(rootWidth / gcdWidth + 0.5)
	
	-- local gcdHeightWidth = self:gcd(gcdHeight, gcdWidth)
	-- local lcm = math.floor(gcdHeight / gcdHeightWidth + 0.5) * gcdWidth

	-- if lcm > self.gridWidth then
	-- 	lcm = math.min(gcdHeight, gcdWidth)
	-- end

	-- -- logError("test gcdHeight: "..gcdHeight.."  "..tostring(rootHeight).."  "..tostring(self._funObj:GetPanel().height))
	-- -- logError("test gcdWidth: "..gcdWidth.."  "..tostring(rootWidth).."  "..tostring(self._funObj:GetPanel().width))
	-- -- logError("test gcdHeightWidth: "..gcdHeightWidth.."  "..tostring(gcdHeight).."  "..tostring(gcdWidth))
	-- -- logError("test lcm: "..lcm)
	-- -- logError("test self.gridWidth: "..self.gridWidth)

	-- if self.gridWidth >= lcm then
	-- 	self.gridWidth = math.floor(self.gridWidth / lcm) * lcm
	-- end
	-- use gcd end

	self.gridWidth = math.floor(self.gridWidth)

	self.chessboardHeight = self.gridWidth * rowCount
	self.chessboardWidth = self.gridWidth * colCount


	self.halfGridWidth = self.gridWidth / 2


	self._dataObj.chessboardPanel.baseClipRegion = Vector4(0, 0, self.chessboardWidth, self.chessboardHeight)
	self.gridDatas = {}

	-- math.randomseed(0)

	for i = 0,rowCount - 1 do
		for j = 0,colCount - 1 do
			local gridIndex = i * colCount + j
			if chessGridData[gridIndex + 1] ~= 0 then
				local newGridBg = InstantiateNew(self._dataObj.gridbg)
				newGridBg.transform:SetParent(self._dataObj.chessboard.transform, false)
				newGridBg.gameObject:SetActive(true)
				newGridBg.width = self.gridWidth
				newGridBg.height = self.gridWidth
				local x, y = self:GetGridPos(j, i)
				newGridBg.transform.localPosition = Vector3(x, y, 0)

				local gridType = nil
				if chessGridData[gridIndex + 1] > 1 then
					gridType = chessGridData[gridIndex + 1]
				-- else
				-- 	self.gridDatas[gridIndex + 1] = {gridType = 0}
				-- 	while true do
				-- 		self.gridDatas[gridIndex + 1].gridType = self:RandomGridType()
				-- 		local len = self:CheckClearUpForOneDir(j, i, -1, 0)
				-- 		local len2 = self:CheckClearUpForOneDir(j, i, 0, -1)
				-- 		if len < 2 and len2 < 2 then
				-- 			gridType = self.gridDatas[gridIndex + 1].gridType
				-- 			break
				-- 		end
				-- 	end	
				end

				
				self.gridDatas[gridIndex + 1] = self:CreateGridData(j, i, gridType)
			else
				self.gridDatas[gridIndex + 1] = nil
			end
		end
	end


	

	UIEventListener.Get(self._dataObj.chessboard.gameObject).onPress = UIEventListener.BoolDelegate(P.OnPress, self);
	-- UpdateBeat:Add(self.Update, self)

	UIEventListener.Get(self._dataObj.closeBtn.gameObject).onClick = UIEventListener.VoidDelegate(P.OnClickBack, self)

	UIEventListener.Get(self._dataObj.successTipObj.gameObject).onClick = UIEventListener.VoidDelegate(P.OnClickBack, self)

	while self:CheckConnect() do
		self:ReRandomRotateGrid()
	end




	self._dataObj.successConditionWidget.gameObject:SetActive(true)
	self._dataObj.successConditionWidget.transform:DOLocalMoveY(100, 0.5, false):From()
	self._dataObj.successConditionWidget:DOAlpha(0, 0.5):From()

	UIEventListener.Get(self._dataObj.successConditionWidget.gameObject).onClick = UIEventListener.VoidDelegate(P.OnClickConditionBg, self)


	self.conditionShowing = true

	self.state = STATE_0
	coroutine.start(function()
		coroutine.wait(3)
		self:OnClickConditionBg()
	end)

	
end


function P:OnDestroy()
	-- UpdateBeat:Remove(self.Update, self)
	-- PlayMainBGM()
	P.super.OnDestroy(self)
end


function P:CalDirMask(dirMask, dir)
	local tenPowForTurn = math.pow(10, 4 - dir)
	local turnMask = math.floor(dirMask / tenPowForTurn)
	local tenPowForShift = math.pow(10, dir)
	local shiftMask = dirMask % tenPowForTurn
	local newDirMask = shiftMask * math.pow(10, dir) + turnMask
	return newDirMask
end


function P:CheckConnect(showDialog)
	local addedQueueSet = {}
	local queue = {}
	local exitCount = 0
	for k,v in pairs(self.gridDatas) do
		if clearupGameConfig[v.gridType].entrance then
			queue[#queue + 1] = k - 1
			addedQueueSet[k - 1] = true
		end

		if clearupGameConfig[v.gridType].exit then
			exitCount = exitCount + 1
		end
	end

	local i = 1
	while i <= #queue do
		local t = queue[i]
		i = i + 1

		local dir = self.gridDatas[t + 1].dir
		local dirMask = clearupGameConfig[self.gridDatas[t + 1].gridType].dirMask
		dirMask = self:CalDirMask(dirMask, dir)

		local x, y = self:GridIndexToGridPos(t)

		local temp = dirMask
		for i = 0,3 do
			if temp % 10 ~= 0 then
				local tx = x
				local ty = y
				if i == 0 then -- left
					tx = tx + 1
				elseif i == 1 then -- up
					ty = ty - 1
				elseif i == 2 then -- right
					tx = tx - 1
				else -- down
					ty = ty + 1
				end

				if self:isGridPointValid(tx, ty) then
					local tempIndex = self:GridPosToGridIndex(tx, ty)
					if not addedQueueSet[tempIndex] then
						local tempDir = self.gridDatas[tempIndex + 1].dir
						local tempDirMask = clearupGameConfig[self.gridDatas[tempIndex + 1].gridType].dirMask

						tempDirMask = self:CalDirMask(tempDirMask, tempDir)

						local tenPowForNeigbor = math.pow(10, (i + 2) % 4)
						tempDirMask = math.floor(tempDirMask / tenPowForNeigbor)
						if tempDirMask % 10 ~= 0 then
							addedQueueSet[tempIndex] = true
							queue[#queue + 1] = tempIndex
						end
					end
				end
			end

			temp = math.floor(temp / 10)
		end
	end


	local connectedExitCount = 0
	for k,v in pairs(self.gridDatas) do
		if addedQueueSet[k - 1] then
			v.gridObj:SetColor(0x0bfffeff)

			if clearupGameConfig[v.gridType].exit then
				connectedExitCount = connectedExitCount + 1
			end
		else
			v.gridObj:SetColor(0xffffffff)
		end
	end

	if exitCount == connectedExitCount then


		if showDialog then
			self.successed = true

			self._dataObj.successEffectObj.gameObject:SetActive(true)

			coroutine.start(function()
				coroutine.wait(2)
				
				self._dataObj.successEffectObj.gameObject:SetActive(false)
				self._dataObj.successTipObj.gameObject:SetActive(true)

				self.boolIswin = true
			end)

			-- local winProxy2 = RootWindowManager.instance:ShowWindow("OneBtnDialog", false);
			-- winProxy2:Init({content="clearup success", okBtnText=LuaTextKey.ok});

			
			-- if self.finishCallback then
			-- 	local temp = self.finishCallback
			-- 	self.finishCallback = nil

			-- 	temp(self, true)
			-- end
		else
			return true
		end
	end
end


function P:RandomGridType()
	local rainbowRate = math.random()

	local color = 0
	if rainbowRate > 0.95 then
		color = RAINBOW_ID
	else
		color = math.random(self.chessTypeCount)
		-- color = math.random(2)
	end



	local t = 0
	if color ~= RAINBOW_ID then
		local specialRate = math.random()
		if specialRate > 0.95 then
		-- if specialRate > 1 then
			t = math.random(HOR, BOMB)
		else
			t = 1
		end
	end

	return color * 10 + t
end

function P:SplitGridType(value)
	local t = math.floor(value / 10000)
	local dirmask = value % 10000
	return t, dirmask
end

function P:CreateGridData(gx, gy, gridType, prefab)
	local newGrid = InstantiateNew(prefab or self._dataObj.grid)
	newGrid.transform:SetParent(self._dataObj.chessboard.transform, false)
	newGrid.gameObject:SetActive(true)
	newGrid.width = self.gridWidth
	newGrid.height = self.gridWidth
	local x, y = self:GetGridPos(gx, gy)
	newGrid.transform.localPosition = Vector3(x, y, 0)
	local dir = math.random(0, 3)
	newGrid.transform.localRotation = Quaternion.Euler(0, 0, 90 * dir)

	gridType = gridType or self:RandomGridType()

	if gridType == nil or clearupGameConfig[gridType] == nil then
		logError("gridType is nil: "..tostring(gridType))
	end

	if clearupGameConfig[gridType].icon then
		newGrid:SetSprite(clearupGameConfig[gridType].icon)
	end
	local gridData = {gridObj = newGrid, gridType = gridType, dir = dir}
	return gridData
end

function P:ReRandomRotateGrid()
	for i = 0,rowCount - 1 do
		for j = 0,colCount - 1 do
			local gridIndex = i * colCount + j
			local gridData = self.gridDatas[gridIndex + 1]
			local dir = math.random(0, 3)
			
			gridData.gridObj.transform.localRotation = Quaternion.Euler(0, 0, 90 * dir)
			gridData.dir = dir
		end
	end
end


function P:GridPosToGridIndex(x, y)
	return y * colCount + x
end

function P:GridIndexToGridPos(index)
	local x = index % colCount
	local y = math.floor(index / colCount)
	return x, y
end

function P:GetGridPos(x, y)
	return self.halfGridWidth + x * self.gridWidth - self.chessboardWidth / 2, self.halfGridWidth + (rowCount - y - 1) * self.gridWidth  - self.chessboardHeight / 2
end


function P:ScreenToChessboardPoint(s)
	local mouseScreenPos = s
	local camera = self._funObj:GetPanel().anchorCamera
	mouseScreenPos.z = -camera.transform.position.z
	local worldPosition = camera:ScreenToWorldPoint(mouseScreenPos)
	local localPos = self._dataObj.chessboard.transform:InverseTransformPoint(worldPosition)
	return localPos
end

function P:ChessboardToGridPoint(c)
	local x = math.floor((c.x + self.chessboardWidth / 2) / self.gridWidth)
	local y = rowCount - 1 - math.floor((c.y + self.chessboardHeight / 2) / self.gridWidth)

	return x, y
end



function P:isGridPointValid(x, y)
	return x >= 0 and x < colCount and y >= 0 and y < rowCount and self.gridDatas[self:GridPosToGridIndex(x, y) + 1] ~= nil
end

function P:isGridPointCanMove(x, y)
	return self:isGridPointValid(x, y)
end



function P:OnPress(obj, state)
	if self.state ~= STATE_1 then
		return
	end

	if self.successed then
		return
	end

	if state then
		self.pressStartPos = nil


		local startPos = self:ScreenToChessboardPoint(UnityEngine.Input.mousePosition)
		local gx, gy = self:ChessboardToGridPoint(startPos)
		if self:isGridPointCanMove(gx, gy) then
			self.pressStartPos = startPos
		end
	elseif self.pressStartPos then
		local startPos = self.pressStartPos
		self.pressStartPos = nil

		local endPos = self:ScreenToChessboardPoint(UnityEngine.Input.mousePosition)
		local gx, gy = self:ChessboardToGridPoint(startPos)
		local gx2, gy2 = self:ChessboardToGridPoint(startPos)
		
		if gx == gx2 and gy == gy2 then
			local index = self:GridPosToGridIndex(gx, gy)
			local dir = self.gridDatas[index + 1].dir
			dir = (dir + 1) % 4
			self.gridDatas[index + 1].dir = dir
			self.gridDatas[index + 1].gridObj.transform.localRotation = Quaternion.Euler(0, 0, 90 * dir)

			self:CheckConnect(true)
		end
	end
end

function P:OnClickBack()
	if self.finishCallback then
		local temp = self.finishCallback
		self.finishCallback = nil

		temp(self, self.boolIswin)
	end
	self._funObj:Close()
end


function P:OnClickConditionBg()
	if self.conditionShowing then
		self.conditionShowing = false
		self.state = STATE_1

		self._dataObj.successConditionWidget.transform:DOLocalMoveY(100, 0.5, false)
		self._dataObj.successConditionWidget:DOAlpha(0, 0.5)

		coroutine.start(function()
			coroutine.wait(0.5)
			if self._dataObj then
				self._dataObj.successConditionWidget.gameObject:SetActive(false)
			end
		end)
	end
end